package cn.facetoken.homer;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.nnf.nnfkitlib.NNF_FaceInfo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import cn.openbiotoken.BiometricsFactory;
import cn.openbiotoken.BiometricsFeature;
import cn.openbiotoken.BiometricsFeatureExtractor;
import cn.openbiotoken.android.UriBitmapHelper;
import cn.openbiotoken.impl.BiometricsFeatureImpl;

public abstract class AbstractBiometricsRecognitionFragment extends Fragment implements View.OnClickListener, PhotoTakingPrincipal, TopSimilarSampleWindow/*, View.OnCreateContextMenuListener, MenuItem.OnMenuItemClickListener*/ {

    private ImageButton takenPhotoView;
    private ImageButton takenVideoView;
    private ImageButton takenImageView;

    private ImageView matchedPhotoView;
    private TextView matchedNameTextView;
    private TextView similarityTextView;

    private PhotoTaker photoTaker;

    public static final int REQUEST_TAKE_PHOTO = 1;
    /*
        private static final byte ITEM_ID_TAKE_PHOTO = 1;
        private static final byte ITEM_ID_TAKE_VIDEO = 2;
    */

    public static final float SIMILARITY_VALVE = 0.6F;

    @Override
    public void onCreate(@Nullable final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        CollapsingToolbarLayout appBarLayout = requireActivity().findViewById(R.id.toolbar_layout);
        if (appBarLayout != null) {
            appBarLayout.setTitle(getResources().getString(getTitleResource()));
        }
    }

    @NonNull
    protected abstract BiometricsFactory<NNF_FaceInfo, float[], Uri> createBiometricsFactory();

    protected abstract int getTitleResource();

    protected abstract int getLayout();

    @Override
    public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container,
                             @Nullable final Bundle savedInstanceState) {
        final View rootView = inflater.inflate(getLayout(), container, false);
        Log.i(getClass().getSimpleName(), String.format("Root view %s is inflated.", rootView.getClass().getName()));

        takenPhotoView = rootView.findViewById(R.id.taken_photo_view);
        takenPhotoView.setOnClickListener(this);
        //takenPhotoView.setOnCreateContextMenuListener(this);
        if (null == photoTaker) {
            photoTaker = new PhotoTaker(this, requireActivity(), takenPhotoView);
        }

        takenVideoView = rootView.findViewById(R.id.taken_video_view);
        takenVideoView.setOnClickListener(this);

        takenImageView = rootView.findViewById(R.id.taken_image_view);
        takenImageView.setOnClickListener(this);

        matchedPhotoView = rootView.findViewById(R.id.matched_photo_view);
        matchedNameTextView = rootView.findViewById(R.id.matched_name_text);
        similarityTextView = rootView.findViewById(R.id.similarity_text);

        return rootView;
    }

    @Override
    public void onClick(@NonNull final View v) {
        Log.i(getClass().getSimpleName(), "onClick(): Fragment was clicked.");
        if (v == takenPhotoView) {
            Log.i(getClass().getSimpleName(), "onClick(): Taken Photo View on Fragment was clicked.");
            takePhoto();
        } else if (v==takenVideoView) {
            Log.i(getClass().getSimpleName(), "onClick(): Taken Video View on Fragment was clicked.");
            takeVideoActivity();
        } else if (v==takenImageView) {
            Log.i(getClass().getSimpleName(), "onClick(): Taken Image View on Fragment was clicked.");
            takeImage();
        } else {
            Log.i(getClass().getSimpleName(), "onClick(): Fragment was clicked, but not on the corresponding views.");
        }
    }
    /*
        @Override
        public void onCreateContextMenu(@NonNull final ContextMenu menu, @NonNull final View view, @Nullable final ContextMenu.ContextMenuInfo menuInfo) {
            menu.setHeaderTitle(loadStringResource(R.string.menu_header_title));

            addMenuItem(menu, ITEM_ID_TAKE_PHOTO, R.string.take_photo);
            addMenuItem(menu, ITEM_ID_TAKE_VIDEO, R.string.take_video);
        }

        private void addMenuItem(@NonNull final ContextMenu menu, final byte itemId, final int resourceId) {
            @NonNull final MenuItem menuItem = menu.add(itemId / 10, itemId, itemId, loadStringResource(resourceId));
            menuItem.setOnMenuItemClickListener(this);
        }

        @NonNull
        private String loadStringResource(final int resourceId) {
            return requireContext().getResources().getString(resourceId);
        }
    */
    @Override
    public void startTakePhoto(@NonNull final Intent intent) {
        startActivityForResult(intent, REQUEST_TAKE_PHOTO);
    }

    @Override
    public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
        Log.d(getClass().getSimpleName(), String.format("onActivityResult(): Request Code = %d, Result Code = %d.", requestCode, resultCode));
        switch (requestCode) {
            case REQUEST_TAKE_PHOTO:
                Uri photoUri = photoTaker.onPhotoTaken(resultCode, data);
                findPhoto(photoUri);
                break;
            case REQUEST_TAKE_PHOTO + 1:
            default:
        }
    }

    private void takePhoto() {
        photoTaker.takePhoto();
    }

    private void findPhoto(@NonNull final Uri photoUri) {
        Log.d(getClass().getSimpleName(), String.format("findPhoto(): Photo URI = %s.", photoUri));
        @NonNull final Activity activity = requireActivity();
        @NonNull final TopSimilarSampleWindow window = this;
        new Thread(
                () -> {
                    List<FaceSample> samples = MainApplication.getMainDatabase().faceSampleDao().loadAll();

                    @NonNull final BiometricsFactory<NNF_FaceInfo, float[], Uri> factory = createBiometricsFactory();
                    @NonNull final BiometricsFeatureExtractor<float[]> extractor = factory.createFeatureExtractor(photoUri);
                    @NonNull final BiometricsFeature<float[]> sampleFeature = extractor.extract();
                    @NonNull final List<BiometricsFeature<float[]>> library = new ArrayList<>(samples.size());
                    for (FaceSample sample : samples) {
                        BiometricsFeature<float[]> appFeature = new BiometricsFeatureImpl<>();
                        appFeature.setFeature(sample.getFaceFeature());
                        appFeature.putExtra(BiometricsFeature.EXTRA_KEY_SAMPLE_ID, sample.id);
                        library.add(appFeature);
                    }

                    final BiometricsFeature<float[]> matched = extractor.top(sampleFeature, Collections.unmodifiableList(library), SIMILARITY_VALVE);
                    Log.d(getClass().getSimpleName(), String.format("findPhoto() Thread.run(): matched = %s.", matched));

                    final float topSimilarity = null == matched ? 0.0F : matched.getFloatExtra(BiometricsFeature.EXTRA_KEY_SIMILARITY);
                    Log.d(getClass().getSimpleName(), String.format("findPhoto() Thread.run(): Top Similarity = %f.", topSimilarity));
                    final FaceSample topSimilarSample = null == matched ? null : samples.stream().filter(sample -> matched.getLongExtra(BiometricsFeature.EXTRA_KEY_SAMPLE_ID) == sample.id).findFirst().orElse(null);
                    activity.runOnUiThread(
                            () -> window.updateTopSample(topSimilarity, topSimilarSample, photoUri)
                    );
                }
        ).start();
    }

    @Override
    public void updateTopSample(final float topSimilarity, @Nullable final FaceSample topSimilarSample, @NonNull final Uri photoUri) {

        Log.d(getClass().getSimpleName(), String.format("updateTopSample(): Top Similarity = %f.", topSimilarity));
        if (null == topSimilarSample) {
            Log.d(getClass().getSimpleName(), "updateTopSample(): No similar sample was found, return.");
            updateNotMatched();
            return;
        }

        if (topSimilarity > SIMILARITY_VALVE) {
            final float similarity = topSimilarity * 100;
            Log.d(getClass().getSimpleName(), String.format("updateTopSample(): Similarity = %f%%.", similarity));
            @NonNull final UriBitmapHelper helper = new UriBitmapHelper(requireContext(), Uri.parse(Objects.requireNonNull(topSimilarSample.sampleUri)));
            final Bitmap bitmap = helper.loadBitmap();
            if (null!=bitmap) {
                matchedPhotoView.setImageBitmap(bitmap);
            } else {
                matchedPhotoView.setImageResource(R.drawable.ic_launcher_foreground);
            }

            similarityTextView.setText(requireActivity().getResources().getString(R.string.label_similarity_matched, similarity));
            matchedNameTextView.setText(topSimilarSample.name);

            @NonNull final UriBitmapHelper helper2 = new UriBitmapHelper(requireContext(), photoUri);
            takenPhotoView.setImageBitmap(helper2.loadBitmap());
        } else {
            Log.d(getClass().getSimpleName(), String.format("updateTopSample(): Top Similarity = %f. It's too low, return.", topSimilarity));
            updateNotMatched();
        }
    }

    private void updateNotMatched() {
        matchedPhotoView.setImageResource(android.R.drawable.ic_menu_help);
        similarityTextView.setText(R.string.label_similarity_not_matched);
        matchedNameTextView.setText(R.string.label_matched_name);
        takenPhotoView.setImageResource(android.R.drawable.ic_input_add);
    }
    /*
        @Override
        public boolean onMenuItemClick(MenuItem item) {
            switch (item.getItemId()) {
                case ITEM_ID_TAKE_PHOTO:
                    onTakePhotoMenuItemClicked();
                    break;
                case ITEM_ID_TAKE_VIDEO:
                    onTakeVideoMenuItemClicked();
                    break;
                default:
            }
            return true;
        }

        private void onTakePhotoMenuItemClicked() {
            takePhoto();
        }

        private void onTakeVideoMenuItemClicked() {
            takeVideo();
        }

        private void takeVideo() {
            Log.i(getClass().getSimpleName(), "takeVideo(): Start to show Face Camera Fragment.");

            @NonNull final Fragment fragment = new FaceCameraFragment();
            Log.i(getClass().getSimpleName(), "takeVideo(): Succeeded to create a Face Camera Fragment.");

            @NonNull final FragmentTransaction transaction = requireActivity().getSupportFragmentManager().beginTransaction();
            Log.i(getClass().getSimpleName(), "takeVideo(): Succeeded to get the Fragment Transaction.");

            transaction.hide(this);
            Log.i(getClass().getSimpleName(), "takeVideo(): Succeeded to hide the Face Recognition Fragment from the Fragment Transaction.");

            transaction.replace(R.id.demonstration_container, fragment, "FaceCamera");
            Log.i(getClass().getSimpleName(), "takeVideo(): Succeeded to add the Face Camera Fragment into the Fragment Transaction.");

            transaction.show(fragment);
            Log.i(getClass().getSimpleName(), "takeVideo(): Succeeded to show the Face Camera Fragment.");

            transaction.commit();
            Log.i(getClass().getSimpleName(), "takeVideo(): Succeeded to commit the Fragment Transaction.");

            //.commit();
            //requireActivity().getSupportFragmentManager().beginTransaction().hide(this).add(R.id.demonstration_container, new FaceCameraFragment(), "FaceCamera");
        }
    */
    private void takeVideoActivity() {
        Log.i(getClass().getSimpleName(), "takeVideo(): Start to show Face Camera Activity.");

        startActivity(new Intent(requireContext(), FaceCameraActivity.class));
    }

    private void takeImage() {
        Log.i(getClass().getSimpleName(), "takeImage(): Start to choose an image from gallery.");
    }
}
